/**
 * 
 */
package dh.abs.info;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.InvalidPropertiesFormatException;
import java.util.List;
import java.util.Properties;

import dh.abs.SuspendException;
import dh.abs.util.SimulationLogger;
import dh.abs.util.SimulationRandom;

/**
 * This simulation model is based on following paper.
 * Y. Fujita, Y. Washida, F. Toriumi, K. Ueda, K. Ishii,
 * Reversal of Influence: Decrease of Innovator's Influence under Information Diversification,
 *  Proc. of AAMAS2010(extended abstract)
 * 
 * @author denialhumanity
 *
 */
public class InformationDiffusionSimulation {

	/** Simulation environment */
	InformationDiffusionEnvironment env;
	
	/** logger instance */
	SimulationLogger logger;
	
	/** parameter instance */
	InformationDiffusionParameters param;
	
	/**
	 * Default constructor. All the parameters are initialized by default value.
	 * @throws IllegalParameterException 
	 * 
	 */
	public InformationDiffusionSimulation() throws IllegalParameterException {
		param = new InformationDiffusionParameters();
		init();
	}
	
	/**
	 * Parameters are loaded from input file.
	 * The file must be read by Properties class.
	 * @param file Parameter file that described by XML format and can be read by Properties class.
	 * @throws InvalidPropertiesFormatException
	 * @throws IOException
	 * @throws IllegalParameterException
	 */
	public InformationDiffusionSimulation(File file) throws InvalidPropertiesFormatException, IOException, IllegalParameterException {
		Properties prop = new Properties();
		FileInputStream fis = new FileInputStream(file);
		prop.loadFromXML(fis);
		fis.close();
		param = new InformationDiffusionParameters(prop);
		init();
	}
	
	/**
	 * Initialize simulation environment.
	 * @throws IllegalParameterException
	 */
	private void init() throws IllegalParameterException {
		env = new InformationDiffusionEnvironment();
		env.setCurrentStep(param.getCurrentStep());
		env.setMaxStep(param.getMaxStep());
		
		if(param.isResume()) {
			loadAgentData(param.getResumeDir());
		} else {
			env.addAgentCollection(createAgentData());
		}
		logger = new SimulationLogger();
		logger.setPrefix(param.getLogPrefix());
		
		if(param.isAgentLogging()) {
			for(InformationDiffusionAgent agent: env.getAgentList()) {
				logger.addObject(agent);
			}
		}
		
		if(param.isEnvLogging()) {
			logger.addObject(env);
		}
	}
	
	/**
	 * When the simulation is resumed, all the agents and information state are loaded.
	 * parameter str is the directory path that contains resume files.
	 * @param str
	 */
	private void loadAgentData(String str) {
		File resumeDir = new File(str);
		
		File[] files = resumeDir.listFiles();
		for(int i = 0; i < files.length; i++) {
			if(files[i].getName().contains(InformationDiffusionAgent.class.getSimpleName())) {
				InformationDiffusionAgent agent = new InformationDiffusionAgent(env,files[i]);
				env.addAgent(agent);
			}
		}
		
		for(InformationDiffusionAgent agent: env.getAgentList()) {
			File f = new File(str + File.separator + InformationDiffusionAgent.class.getSimpleName() + agent.getID());
			
			BufferedReader br = null;
			
			try {
				br = new BufferedReader(new FileReader(f));
				String line = null;
				
				while((line = br.readLine()) != null) {
					String[] ids = line.split(":");
					String[] agents = ids[1].split(" ");
					
					List<InformationDiffusionAgent> tmpList = new ArrayList<InformationDiffusionAgent>();
					for(int i = 0; i < agents.length; i++) {
						tmpList.add(env.getAgentByID(agents[i]));
					}
					
					Information info = new Information(ids[0], tmpList);
					agent.extendInformation(info);
				}
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * If the simulation is not resumed, all the agents are created randomly.
	 * @return
	 * @throws IllegalParameterException
	 */
	private List<InformationDiffusionAgent> createAgentData() throws IllegalParameterException {
		List<InformationDiffusionAgent> agentList = new ArrayList<InformationDiffusionAgent>();
		List<Double> categList = param.getCategorization();
		List<Integer> infoCapaList = param.getInformationCapacity();
		int currentCapa = 0;
		int currentNum = 0;
		int numAgent = param.getNumAgents();
		
		for(int i = 0; i < numAgent; i++) {
			if((categList.get(currentCapa) * numAgent) <= currentNum) {
				currentNum = 0;
				currentCapa++;
				if(currentCapa >= categList.size()) {
					throw new IllegalParameterException("parameter (categorization or informationCapacity) is invalid");
				}
			}
			InformationDiffusionAgent agent = new InformationDiffusionAgent(env, String.valueOf(i), infoCapaList.get(currentCapa)); 
			agent.addInformationCollection(createInformationList(agent, infoCapaList.get(currentCapa)));
			agentList.add(agent);
			
			currentNum++;
		}
		
		return agentList;
	}
	
	/**
	 * If the simulation is not resumed, all the information is created according to parameters.
	 * @param agent
	 * @param infoCapacity
	 * @return
	 */
	private List<Information> createInformationList(InformationDiffusionAgent agent, int infoCapacity) {
		List<Information> infoList = new ArrayList<Information>();
		
		for(int i = 0; i < infoCapacity; i++) {
			infoList.add(new Information(String.valueOf(SimulationRandom.getInstance().getRand().nextInt(param.getNumInformation())), agent));
		}
		
		return infoList;
	}
	
	
	/**
	 * start simulation according to loaded parameters.
	 * if the isResume parameter is true, all the states are stored to specified files.
	 * if not, final results are output.
	 */
	public void startSimulation() {
		int interval = param.getLoggingInterval();
		logger.logging();
		while(env.getCurrentStep() < env.getMaxStep()) {
			env.preStep();
			env.nextStep();
			env.postStep();
			
			if((--interval)<=0) {
				logger.logging();
				interval = param.getLoggingInterval();
			}
		}
		
		if(param.isSuspend()) {
			File file = new File(param.getSuspendDir() + File.separator + env.getClass().getSimpleName() + ".dat");
			try {
				env.storeToFile(file.getCanonicalFile());
				param.setCurrentStep(env.getCurrentStep());
				param.store2xml(param.getSuspendDir() + File.separator + "suspended_parameters.xml");
			} catch (SuspendException e) {
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
		} else {
			env.outputInfluence(new File(param.getResultFile()));
		}
	}

	/**
	 * @param args only one parameter file
	 */
	public static void main(String[] args) {
		
		if(args.length != 1) {
			System.err.println("Usage: java -jar InformationDiffusionSimulation.jar paramters.xml");
		} else {
			File file = new File(args[0]);
			if(!file.exists() || !file.isFile()) {
				System.err.println(args[0] + " is not valid file.");
			} else {
				try {
					InformationDiffusionSimulation sim = new InformationDiffusionSimulation(file);
					sim.startSimulation();
				} catch (IllegalParameterException e) {
					e.printStackTrace();
				} catch (InvalidPropertiesFormatException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		
	}

}
